package ro.ubbcluj.cs.damate.services.mail;

import org.apache.commons.io.IOUtils;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Scope;
import org.springframework.core.task.SimpleAsyncTaskExecutor;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.mail.javamail.MimeMessagePreparator;
import org.springframework.stereotype.Component;
import ro.ubbcluj.cs.damate.persistence.entities.User;
import ro.ubbcluj.cs.damate.webutils.MessageProvider;

import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
import java.io.IOException;
import java.io.InputStream;
import java.util.concurrent.Callable;
import java.util.concurrent.Future;

import static org.springframework.beans.factory.config.BeanDefinition.*;

@Component
@Scope(SCOPE_PROTOTYPE)
public class MailService {

    private enum MailTemplate {
        REGISTRATION_CONFIRMATION("registrationConfirmation"),
        PASSWORD_RESET("passwordReset"),
        ACCOUNT_ACTIVATION("accountActivation"),
        ACCOUNT_DEACTIVATION("accountDeactivation");

        private String templateFileName;

        private MailTemplate(String templateFileName) {
            this.templateFileName = templateFileName;
        }

        public String getTemplateFileName() {
            return templateFileName;
        }
    }

    @Autowired
    private JavaMailSender mailSender;

    @Autowired
    private SimpleAsyncTaskExecutor asyncTaskExecutor;

    @Autowired
    @Qualifier("mailServiceFromAddress")
    private String fromAddress;

    @Autowired
    @Qualifier("mailServiceFromAlias")
    private String fromAlias;

    private Logger log = Logger.getLogger(MailService.class);

    public void sendRegistrationConfirmation(User newUser) {
        String mailTemplate = readMailTemplate(MailTemplate.REGISTRATION_CONFIRMATION.getTemplateFileName(), "en");
        String mailBody = insertUserDetails(mailTemplate, newUser);
        sendEmail(fromAddress, fromAlias, newUser.getEmail(), MessageProvider.getValue("mail.subject.registrationSuccessful"), mailBody);
    }

    public void sendPasswordReset(User newUser, String newPassword) {
        String mailTemplate = readMailTemplate(MailTemplate.PASSWORD_RESET.getTemplateFileName(), "en");
        String mailBody = insertUserDetails(mailTemplate, newUser, newPassword);
        sendEmail(fromAddress, fromAlias, newUser.getEmail(), MessageProvider.getValue("mail.subject.passwordReset"), mailBody);
    }

    public void sendAccountActivation(User newUser) {
        String mailTemplate = readMailTemplate(MailTemplate.ACCOUNT_ACTIVATION.getTemplateFileName(), "en");
        String mailBody = insertUserDetails(mailTemplate, newUser);
        sendEmail(fromAddress, fromAlias, newUser.getEmail(), MessageProvider.getValue("mail.subject.accountActivation"), mailBody);
    }

    public void sendAccountDeactivation(User newUser) {
        String mailTemplate = readMailTemplate(MailTemplate.ACCOUNT_DEACTIVATION.getTemplateFileName(), "en");
        String mailBody = insertUserDetails(mailTemplate, newUser);
        sendEmail(fromAddress, fromAlias, newUser.getEmail(), MessageProvider.getValue("mail.subject.accountDeactivation"), mailBody);
    }

    private String readMailTemplate(String templateName, String locale) {
		String result = "";

		InputStream templateStream = Thread.currentThread().getContextClassLoader().getResourceAsStream("mailTemplates/"+templateName + "_" + locale + ".html");
        try {
            result = IOUtils.toString(templateStream);
        } catch (IOException e) {
            log.error(e.getMessage(), e);
        }

        return result;
	}

    private String insertUserDetails(String template, User user) {
        template = template.replaceAll("<%username%>", user.getUsername());
		template = template.replaceAll("<%firstname%>", user.getFirstname());
		template = template.replaceAll("<%lastname%>", user.getLastname());
		template = template.replaceAll("<%sex%>", user.isSex() ? MessageProvider.getValue("RegistrationPage.inputFields.sex.male") : MessageProvider.getValue("RegistrationPage.inputFields.sex.female"));
		template = template.replaceAll("<%age%>", user.getAge().toString());
		template = template.replaceAll("<%email%>", user.getEmail());

		return template;
	}

    private String insertUserDetails(String template, User user, String newPassword) {
        template = template.replaceAll("<%firstname%>", user.getFirstname());
        template = template.replaceAll("<%lastname%>", user.getLastname());
        template = template.replaceAll("<%newPassword%>", newPassword);

        return template;
    }

    /**
     * Send an e-mail to one or multiple recipients. The recipient emails can be split
     * with either , either ;.
     */
    public Future<Boolean> sendEmail(final String fromAddress, final String fromAlias, final String toRecipients, final String subject, final String messageBody) {
        MimeMessagePreparator preparator = new MimeMessagePreparator() {
            public void prepare(MimeMessage mimeMessage) throws Exception {
				MimeMessageHelper message = new MimeMessageHelper(mimeMessage, "UTF-8");
				if (isMultipleRecipients(toRecipients)) {
                    message.setTo(toRecipients.split("\\s*[,;]\\s*"));
                } else {
                    message.setTo(toRecipients);
                }
				message.setFrom(new InternetAddress(fromAddress, fromAlias));
				message.setSubject(subject);
				message.setText(messageBody, true);
			}
		};

        return asyncTaskExecutor.submit(getEmailSenderTask(preparator));
    }

    private boolean isMultipleRecipients(String toRecipients) {
        return toRecipients.contains(",") || toRecipients.contains(";");
    }

    private Callable<Boolean> getEmailSenderTask(final MimeMessagePreparator message) {
        return new Callable<Boolean>() {
            @Override
            public Boolean call() throws Exception {
                try {
                    mailSender.send(message);
                    return true;
                } catch (Exception e) {
                    log.error("Failed to send the e-mail.", e);
                    return false;
                }
            }
        };
    }
}
